//
// Created by 15068 on 2021/5/4.
//

#include <cstdint>
#include <string>
#include <adt.h>

#ifndef LC_3VM_VM_H
#define LC_3VM_VM_H
#endif //LC_3VM_VM_H

typedef enum {
    R_R0 = 0,
    R_R1,
    R_R2,
    R_R3,
    R_R4,
    R_R5,
    R_R6,
    R_R7,
    R_PC,   // program counter
    R_COND, // conditional flag (@enum Flags)
    R_COUNT // registers count
} Registers;

/* Memory Mapped Registers, for control IO devices */
enum
{
    MR_KBSR = 0xFE00, /* keyboard status */
    MR_KBDR = 0xFE02  /* keyboard data */
};

typedef enum {
    FL_POS = 0b001, // positive
    FL_ZRO = 0b010, // zero
    FL_NEG = 0b100  // negative
} Flags;

typedef enum {
    OP_BR,      /* branch */
    OP_ADD,     /* add  */
    OP_LD,      /* load */
    OP_ST,      /* store */
    OP_JSR,     /* jump register */
    OP_AND,     /* bitwise and */
    OP_LDR,     /* load register */
    OP_STR,     /* store register */
    OP_RTI,     /* unused */
    OP_NOT,     /* bitwise not */
    OP_LDI,     /* load indirect */
    OP_STI,     /* store indirect */
    OP_JMP,     /* jump */
    OP_RES,     /* reserved (unused) */
    OP_LEA,     /* load effective address */
    OP_TRAP     /* execute trap */
} Operators;

typedef enum {
    TRAP_GETC = 0x20,   // Get character from keyboard (not echoed onto terminal)
    TRAP_OUT = 0x21,    // Put out a character
    TRAP_PUTS = 0x22,   // Put out a string
    TRAP_IN = 0x23,     // Get character from keyboard (echoed onto terminal)
    TRAP_PUTSP = 0x24,  // Put out a byte string
    TRAP_HALT = 0x25    // Halt program
} Traps;

// little computer 3 (LC-3) virtual machine
class LC3VM {
private:
    const char* imagePath;
    UINT16 insCount;
    unsigned long imageSize;
    UINT16 origin;

private:
    bool checkPlatform();
    inline void swapEndian(UINT16& ins);
    UINT16 signExtend(UINT16 num, UINT16 bitCount);
    void loadImage();
    UINT16 readMemory(UINT16 pc);
    UINT16 memory[UINT16_MAX] = {0};
    UINT16 regs[R_COUNT];
    bool running = false;
    UINT16 getOrigin() { return this->origin; }
    UINT16 getInsCount() { return this->insCount; }
    // Update conditional flag upon writing to register
    void updateFlag(UINT16 reg);

    void ADD(UINT16 instr);
    void AND(UINT16 instr);
    void NOT(UINT16 instr);
    // Branch, conditional jump
    void BR(UINT16 instr);
    // Jump
    void JMP(UINT16 instr);
    // Jump register
    void JSR(UINT16 instr);
    // Load
    void LD(UINT16 instr);
    // Load
    void LDI(UINT16 instr);
    // Load register
    void LDR(UINT16 instr);
    // Load effective address
    void LEA(UINT16 instr);
    // Store
    void ST(UINT16 instr);
    // Store indirectly
    void STI(UINT16 instr);
    // Store register
    void STR(UINT16 instr);
    void RES();
    void RTI();
    // Trap routine (中断子程序)
    void FPUTS();
    void FGETC();
    void FOUT();
    void FIN();
    void FPUTSP();
    void FHALT();

    void clearLog();
    void exportLog(const char* logFile, const char* mode, const char* log);

public:
    bool start();
    explicit LC3VM(const char* imagePath);

    // Get instruction from current program pointer
    UINT16 fetch();
    // Decode instruction and execute
    void execute(UINT16 instr);

protected:
    LC3VM() = default;
};
